package ink.xiaobaibai.serviceOxy.onePay.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ink.xiaobaibai.entity.OzoneDevice;
import ink.xiaobaibai.enumOxy.DeviceStatus;
import ink.xiaobaibai.service.IOzoneDeviceService;
import ink.xiaobaibai.serviceOxy.onePay.IGeoDeviceService;
import ink.xiaobaibai.vo.DeviceByGeoVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.GeoResults;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: 地理位置查询实现类
 * @author: 小白白
 * @create: 2021-05-22
 **/

@Service
public class GeoDeviceServiceImpl implements IGeoDeviceService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IOzoneDeviceService ozoneDeviceService;

    public static final String GEOKEY = "GEO_KEY";

    @Override
    public void addDevice(OzoneDevice ozoneDevice) {
        //需要保证内部地点不能重复
        this.add(ozoneDevice);
    }

    @Override
    public void updateDevice(OzoneDevice ozoneDevice) {
        //修改机器
        //先删除
        this.remove(ozoneDevice);
        //再新添加
        this.add(ozoneDevice);
    }

    @Override
    public void deleteDevice(OzoneDevice ozoneDevice) {
        this.remove(ozoneDevice);
    }

    private void add(OzoneDevice ozoneDevice) {
        this.stringRedisTemplate.opsForGeo().add(
                GEOKEY, new Point(ozoneDevice.getLng(), ozoneDevice.getLat()),
                //存入机器Id
                ozoneDevice.getId().toString());
    }

    private void remove(OzoneDevice ozoneDevice) {
        this.stringRedisTemplate.opsForGeo().remove(GEOKEY, ozoneDevice.getId().toString());
    }

    /**
     * 以给定的城市为中心， 返回键包含的位置元素当中，
     * 与中心的距离不超过给定最大距离的所有位置元素，
     * 并给出所有位置元素与中心的平均距离。
     *
     * @param lng
     * @param lat
     * @return
     */
    @Override
    public List<DeviceByGeoVo> getDeviceListNearbyGeo(Double lng, Double lat, Double kmLength) {

        //中心点
        Point centerPoint = new Point(lng, lat);
        //距离
        Distance distance = new Distance(kmLength, RedisGeoCommands.DistanceUnit.KILOMETERS);
        //封装
        Circle circle = new Circle(centerPoint, distance);
        //查询参数
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs
                .newGeoRadiusArgs()
                //查询结果包含距离
                .includeDistance()
                //查询结果包含坐标
                .includeCoordinates()
                //查询出的结果按照距离排序,无需进行排序
                .sortAscending()
                //此limit和上方排序配合使用,可避免一次性查出的数据过多
                .limit(100);
        //开始查询
        GeoResults<RedisGeoCommands.GeoLocation<String>> data =
                this.stringRedisTemplate.opsForGeo().radius(GEOKEY, circle, args);

        List<DeviceByGeoVo> list = new ArrayList<>();
        Map<Integer, DeviceByGeoVo> map = new HashMap<>(list.size());
        List<Integer> ids = new ArrayList<>();
        if (data != null) {
            data.forEach(geoLocationGeoResult -> {
                RedisGeoCommands.GeoLocation<String> content = geoLocationGeoResult.getContent();
                //member 名称
                Integer id = Integer.parseInt(content.getName());
                // 对应的经纬度坐标
                Point point = content.getPoint();
                // 距离中心点的距离
                Distance dis = geoLocationGeoResult.getDistance();
                DeviceByGeoVo deviceByGeoVo = this.obtainOzoneDevice(id, point, dis);
                list.add(deviceByGeoVo);
                map.put(deviceByGeoVo.getDeviceId(), deviceByGeoVo);
                ids.add(deviceByGeoVo.getDeviceId());
            });
        }

        if (list.size() == 0) {
            return list;
        }

        //批量查询后加入地点和状态
        QueryWrapper<OzoneDevice> q1 = new QueryWrapper<>();
        q1.select("id", "address", "status", "work_stop_time");
        q1.in("id", ids);
        List<OzoneDevice> ozoneDeviceList = this.ozoneDeviceService.list(q1);

        for (int i = 0; i < ozoneDeviceList.size(); i++) {
            map.get(ozoneDeviceList.get(i).getId()).setAddress(ozoneDeviceList.get(i).getAddress());
            if (ozoneDeviceList.get(i).getWorkStopTime() != null && ozoneDeviceList.get(i).getWorkStopTime().getTime() > System.currentTimeMillis()) {
                map.get(ozoneDeviceList.get(i).getId()).setStatus(DeviceStatus.INUSE.getStatus());
            } else {
                map.get(ozoneDeviceList.get(i).getId()).setStatus(ozoneDeviceList.get(i).getStatus());
            }
        }

        return list;
    }

    private DeviceByGeoVo obtainOzoneDevice(Integer geoId, Point point, Distance dis) {
        //地点和状态先置为空
        return new DeviceByGeoVo(geoId, dis.getValue(), null, null, point.getX(), point.getY());
    }

}
